package metier;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import utiles.K;
import metier.Element;
import utiles.FourmizLogger;

/**
 * Classe de model de la Carte
 *
 * @author Thomas Folgueral
 * @author Julien Pernot
 * @author Guillaume Peyet
 * @author Guillaume Sainthillier
 */
public class Carte extends Observable implements Observer {

    private String nom;
    private String path;
    private int longueur;
    private int largeur;
    private Environnement e;    
    private Hashtable<Element, Position> map;
    
    /**
     * Constructeur de la carte, un nom doit être fourni en paramètre
     * Initialise une carte de 20x20
     * @param pnom le nom de la carte
     */
    public Carte(String pnom) {
        this(pnom, K.LONG, K.LARG);
    }

    /**
     * Constructeur de la carte, un nom doit être fourni en paramètre
     * @param pnom le nom de la carte
     * @param lignes le nombre de lignes
     * @param cols le nombre de colonnes
     */
    public Carte(String pnom, int lignes, int cols) {
        nom = pnom;
        longueur = lignes;
        largeur = cols;
        map = new Hashtable<Element, Position>();
        e = new Environnement();
    }

    /**
     * Retourne les élèments présents sur une position donnée
     * @param pos la position à vérifier
     * @return les élements présents, null sinon
     */
    public ArrayList<Element> getElements(Position pos) {
    	if (pos == null)
    		return new ArrayList<Element>();
    	
        HashMap<Integer, ArrayList<Element>> elements_temp = new HashMap<Integer, ArrayList<Element>>();
        
        for (Object o : map.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            Position positionO = (Position) entry.getValue();
            
            if (positionO.getX() == pos.getX() && positionO.getY() == pos.getY()) {
                Element elem = (Element) entry.getKey();
                int index = 0;
                switch (elem.getClass().getName()) {
                    case "metier.Herbe":
                        index = 0;
                        break;
                    case "metier.Nouriturre":
                        index = 1;
                        break;
                    case "metier.Roche":
                        index = 2;
                        break;
                    case "metier.Pheromone":
                    	index = 3;
                    	break;
					case "metier.Reine":
                    	index = 4;
                    	break;
                    case "metier.Fourmi":
                        index = 5;
                        break;
                    case "metier.Fourmiliere":
                        index = 6;
                        break;
                }
				
				if (elements_temp.get(index) == null)
					elements_temp.put(index, new ArrayList<Element>());
				
				elements_temp.get(index).add(elem);
				
            }
        }
        
        ArrayList<Element> elements = new ArrayList<Element>();
        for(int i = 0 ; i < 7 ; i++) {
            if (elements_temp.get(i) != null) {
				for (Element e : elements_temp.get(i))
					elements.add(e);
			}
        }
        
        return elements;
    }

    /**
     * Retourne les élèments présents sur la carte
     * @return la liste des élèments
     */
    public ArrayList<Element> getElements() {

        ArrayList<Element> elts = new ArrayList();
        for (Object o : map.entrySet()) {
            Map.Entry entry = (Map.Entry) o;

            elts.add((Element) entry.getKey());
        }        

        return elts;
    }

    /**
     * Retourne la position d'un élèment sur la carte
     * @param elt l'élèment à vérifier
     * @return la position de l'élèment
     */
    public Position getPosition(Element elt) {
        for (Map.Entry entry : map.entrySet()) {
            if (entry.getKey() == elt) {
                return (Position) entry.getValue();
            }
        }
        return null;
    }
    
    @Override
    public void update(Observable elt, Object newpos) {
		Element element = (Element) elt;
		Position newPosition = (Position) newpos;

		if (element != null && newPosition != null) {

			for (Iterator<Map.Entry<Element, Position>> it = map.entrySet()
					.iterator(); it.hasNext();) {
				Map.Entry<Element, Position> entry = it.next();

				if (((Element) entry.getKey()) == element) {
					it.remove();
				}
			}

			this.map.put(element, newPosition);

			this.setChanged();
			this.notifyObservers(element);
		}
    }

    /**
     * Ajoute un élèment sur la carte à la position fournie en argument.<br />
     * Notifie également son/ses observeurs du changement de la carte
     * @param elt l'élèment à positioner
     * @param pos la position souhaitée de l'élèment
     */
    public void addElement(Element elt, Position pos) {
        // on récupère le type de l'élément à insérer
        Class addingClass = elt.getClass();
        boolean exists = false;

        
        for (Element elem : this.getElements(pos)) {
            if (elem.getClass().equals(addingClass))
               exists = true;
        }
        
        if (elt instanceof Fourmi ||
            elt instanceof Reine ||
            elt instanceof Pheromone ||
            elt instanceof Fourmiliere )
        {
            exists = false;
        }        	
        
    	if (!exists && elt != null && pos != null) {                    
            map.put(elt, pos);
            elt.addObserver(this);
    	}
    }
    
    public void removeElements(Position position) {
        for (Element currentElem : this.getElements(position)) {
            this.map.remove(currentElem);
        }
		
		this.setChanged();
		this.notifyObservers(position);
    }
    
    public void removeElement(Element element) {
		Position oldPos = this.getPosition(element);
		this.map.remove(element);
		this.setChanged();
		this.notifyObservers(oldPos);
	}
    
    /*
     * Getters and Setters
     */   
    
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public int getLongueur() {
        return longueur;
    }

    public void setLongueur(int longueur) {
        this.longueur = longueur;
    }

    public int getLargeur() {
        return largeur;
    }

    public void setLargeur(int largeur) {
        this.largeur = largeur;
    }
    
    public Environnement getEnvironnement() {
        return e;
    }

	public void deposerPheromone(Fourmi fourmi, Position direction) {
		Position position = this.getPosition(fourmi);
		this.addElement(new Pheromone(e.getConcentration(), e.getEvaporation(), direction), position);
	}

	public int recolteNourriture(Fourmi fourmi, int quantiteNourritureTransportable) {
		Position posFourmi = this.getPosition(fourmi);
		Nouriturre nourriture = null;
		
		for (Element element : this.getElements(posFourmi)) {
			if (element instanceof Nouriturre)
				nourriture = (Nouriturre) element;
		}
		
		int quantiteRecoltee = 0;
		if (nourriture != null) {
			quantiteRecoltee = nourriture.recolter(quantiteNourritureTransportable);
		}
		
		return quantiteRecoltee;
	}
	
	public int getNbFourmiliere() {
		int nb = 0;
		
		for (Map.Entry entry : map.entrySet()) {
			Element elem = (Element) entry.getKey();
            if (elem instanceof Fourmiliere) {
            	nb++;
            }
		}
		
		return nb;
	}
}
